# 什么是多线程？并行/并发运行某个程序。(伪并行)
# 进程：一个应用程序就是一个进程，可以拥有1个或多个线程。
# 多线程可以显著提高程序的执行效率。
# 真正的并行：多核CPU，多路CPU。

import time, datetime
import threading

# 第一种使用多线程的方式：
# 1. 让某个类继承自threading.Thread类。
# 2. 该类的需要多线程运行的代码必须放在run方法中。run方法是父类的方法，子类必须重写。
# 3. 实例化类时，直接通过类实例调用start()方法来实现多线程，start()方法会在父类中去调用run()。
class ThreadDemo(threading.Thread):
    def run(self):
        now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
        print('线程: %s 正在执行：%s.' %(self.getName(), now))
        time.sleep(1)

# if __name__ == '__main__':
#     for i in range(20):
#         demo = ThreadDemo()
#         demo.start()







class ThreadTwo:
    def a(self, a1, a2):
        print("A方法正在执行.")
        time.sleep(1)

    def b(self, b1):
        print("B方法正在执行.")

    def c(self):
        print("C方法正在执行.")

# 第二种方式使用多线程：直接使用Thread类进行实例化并传递要调用的方法给target参数作为构造参数，即可启动线程。
# 在传递被调用方法的参数时，必须用args参数来指定为元组的形式进行传递，且target参数所调用方法不能加圆括号。
# if __name__ == '__main__':
#     two = ThreadTwo()
#     for i in range(10):
#         # two = ThreadTwo()
#         # print(id(two))
#         threading.Thread(target=two.a, args=(100, 200)).start()
#
#     for i in range(10):
#         two = ThreadTwo()
#         threading.Thread(target=two.b, args=(-100, )).start()






# 正常情况下，一个程序的运行，Python会自己启动一个线程来执行，这个线程称为“主线程”
# 可以通过获取线程的名称，来确认是否为多线程。
# 主线程并非一定是最后结束，需要看代码的实际情况，主线程一旦生成了子线程后，子线程再不受控。
# 线程并不是按严格的生成顺序来执行的，完全依赖于CPU的心情。
# 线程什么时候结束：每一个线程将要执行的代码执行完成后就会结束。
# 只要一个进程中还有一个线程活着，则进程就不会结束。


def test(a, b):
    while True:
        time.sleep(1)
        now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
        print('线程: %s 正在执行：%s.' % (threading.current_thread().getName(), now))
        # time.sleep(1)

for i in range(10):
    t = threading.Thread(target=test, args=(1, 1)).start()

# time.sleep(5)
print("主线程：%s 结束运行." % threading.current_thread().getName())





# 如果主线程需要在所有子线程都运行结束后，才结束? 让主线程sleep足够的时间，让子线程合并到主线程(调用join方法）
# 另外，也有可能需要子线程跟随主线程一直结束，只要主线程结束，子线程也跟着结束。（调用setDaemon方法将子线程设置为守护线程）

# def test():
#     for i in range(6):
#         time.sleep(1)
#         now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
#         print('线程: %s 正在执行：%s.' % (threading.current_thread().getName(), now))
#
# for i in range(10):
#     t = threading.Thread(target=test)
#     t.setDaemon(True)   # 设置该线程是否为守护线程（后台线程），必须在线程开始之前
#     t.start()
#     # t.join()        # 将该线程合并到主线程，必须线程开始之后。
#
# time.sleep(3)
# print("主线程：%s 结束运行." % threading.current_thread().getName())



# class Dirty:
#     class_i = 100
#
#     def __init__(self):
#         self.instance_i = 100
#
#     def test(self):
#         self.instance_i += 1
#         Dirty.class_i += 1
#         # print(self.instance_i)
#         print(Dirty.class_i)
#         time.sleep(1)
#
# if __name__ == '__main__':
#     dirty = Dirty()
#     for i in range(10):
#         # dirty = Dirty()
#         threading.Thread(target=dirty.test).start()



# 多个线程，每一个线程都实例化一个类，或者是先实例化一个类，再启动多个线程，有什么差别？
class MultiTest:
    def __init__(self):
        self.i = 100

    def test(self):
        self.i += 1     # 慎重合作，建议不要让多线程去修改同一个实例空间里面的变量的值，容易出现脏读脏写（Dirty Data）
        print(self.i)

# if __name__ == '__main__':
#     # multi = MultiTest()
#     for i in range(10):
#         multi = MultiTest()
#         threading.Thread(target=multi.test).start()
